#include <iostream>
#include <fstream>

#include "edaBestImprSelect.h"
#include "edaHC.h"
#include "edaSA.h"
#include "edaSeqWrapperControl.h"
#include "edaExpCoolingSchedule.h"
#include "edaGenContinue.h"
#include "loNetList.hpp"
#include "loScriptMove.hpp"
#include "loScriptMoveNext.hpp"
#include "loScriptMoveRandom.hpp"
#include "sisInterface.h"

void printHelp();

int main(int argc, char **argv)
{
  char scriptDir[256];
  char *blifFile = NULL;
  int c;
  int index;

  sprintf(scriptDir, ".");
  while ((c = getopt(argc, argv, "s:h?")) != -1 )
  {
    switch (c)
    {
      case 's':
        strncpy(scriptDir, optarg, 256);
        break;

      case '?':
      case 'h':
      default:
        printHelp();
        return 1;
    }
  }

  if (optind >= argc)
  {
    // There is no BLIF file
    printHelp();
    return 1;
  }

  blifFile = argv[optind];

  srand(time(NULL));

  // start SIS
  sisInit();

  loNetList netlist( blifFile );;

  loScriptMove script1, script2, script3;
  if (scriptDir != NULL)
  {
    script1.setScriptDir(scriptDir);
    script2.setScriptDir(scriptDir);
    script3.setScriptDir(scriptDir);
  }

  loScriptMoveNext scriptNext( 0, 8 );
  edaBestImprSelect moveSelect;
  edaHC hcSearch( &script1,
		  &scriptNext,
		  &moveSelect );

  loScriptMoveRandom script_random1( 0, 5 ), script_random2( 2, 7 );
  edaExpCoolingSchedule coolingSchedule1(0.1, 0.7), coolingSchedule2(0.5, 0.75);
  edaGenContinue cont_criteria1(6), cont_criteria2(7);
  edaSA saSearch1( &script2,
      &script_random1,
      &cont_criteria1,
      800.0,
      &coolingSchedule1);
  edaSA saSearch2( &script3,
      &script_random2,
      &cont_criteria2,
      500.0,
      &coolingSchedule2);

  edaSeqWrapperControl sfControl;
  int hcid1 = sfControl.insertVertex( &hcSearch );
  int hcid2 = sfControl.insertVertex( &hcSearch );
  int said1 = sfControl.insertVertex( &saSearch1 );
  int said2 = sfControl.insertVertex( &saSearch2 );
  sfControl.insertEdge( hcid1, said1 ); 
  sfControl.insertEdge( hcid1, said2 );
  sfControl.insertEdge( said1, hcid2 );
  sfControl.insertEdge( said2, hcid2 );

  std::cout << "[From Fitness] " << netlist.fullEvaluate() << std::endl;
  sfControl.search( netlist );
  std::cout << "[To Fitness] " << netlist.fullEvaluate() << std::endl;

  // save the optimized netlist
  ofstream fp;
  char s[256];
  sprintf( s, "%s.opt", argv[1] );
  fp.open( s );
  netlist.printOn( fp );
  fp.close();

  // end SIS
  sisEnd();

  return 0;
}

edaSerialize *userClassGenerate(int clsid)
{
  switch( clsid ){
    case _USERCLASSID_ + _CLSID_LO_NETLIST_:
      return new loNetList;

    case _USERCLASSID_ + _CLSID_SCRIPT_MOVE_:
      return new loScriptMove;

    case _USERCLASSID_ + _CLSID_SCRIPT_MOVE_RANDOM_:
      return new loScriptMoveRandom;

    case _USERCLASSID_ + _CLSID_SCRIPT_MOVE_NEXT_:
      return new loScriptMoveNext;

    default:
      std::cerr << "Unknown classId " << clsid 
		<< " for object generation!!!" << std::endl;
      exit(-1);
  }
}

void printHelp()
{
  std::cerr << "Usage : ./loSFWSearch [-h] [-?] [-s <scripts dir>] <instance>" << std::endl;
  std::cerr << "  -h, -?             Print this help" << std::endl;
  std::cerr << "  -s <scripts dir>   Directory contains SIS scripts" << std::endl;
  std::cerr << "  <instance>         BLIF file to be optimize" << std::endl;
}
